/////////////////////////////////////////////////////////////////////////////////////////////////////opengl_render.hpp
#ifndef _BLOCK_HPP_
#define _BLOCK_HPP_


#include <iostream>


#include "gl/gl.h"
#include "gl/glext.h"


#include "boost/shared_ptr.hpp"
#include "boost/assert.hpp"
#include "boost/mpl/assert.hpp"
#include "boost/type_traits.hpp"
#include "boost/mpl/if.hpp"
#include "boost/mpl/find.hpp"
#include "boost/mpl/vector.hpp"


namespace  bl = boost::mpl;
namespace render
{
    PFNGLCREATESHADERPROC glCreateShader = 0;
    PFNGLSHADERSOURCEPROC glShaderSource = 0;
    PFNGLCOMPILESHADERPROC glCompileShader = 0;
    PFNGLGETSHADERIVPROC glGetShaderiv = 0;
    PFNGLCREATEPROGRAMPROC glCreateProgram = 0;
    PFNGLATTACHSHADERPROC  glAttachShader = 0;
    PFNGLLINKPROGRAMPROC glLinkProgram = 0;
    PFNGLGETPROGRAMIVPROC  glGetProgramiv = 0;
    PFNGLUSEPROGRAMPROC  glUseProgram = 0;
    PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = 0;
    PFNGLUNIFORM1FPROC glUniform1f = 0;
    PFNGLUNIFORM2FPROC glUniform2f = 0;
    PFNGLUNIFORM3FPROC glUniform3f = 0;
    PFNGLUNIFORM4FPROC glUniform4f = 0;
    PFNGLUNIFORM1IPROC glUniform1i = 0; 
    PFNGLDELETESHADERPROC glDeleteShader = 0;
    PFNGLGENBUFFERSPROC glGenBuffers = 0;
    PFNGLBINDBUFFERPROC glBindBuffer = 0;
    PFNGLBUFFERDATAARBPROC glBufferData = 0;


struct GPUProgram
{
GPUProgram()
{
}
};
struct Viewport
{
Viewport()
{
}
};


struct Perspective
{
Perspective()
{
}
};




    struct OpenGL;
   
    
    typedef float* VertexBuffer;
 
   
    
    //inline void get(VertexBuffer& vb, float* data)
    //{
    //    BOOST_MPL_ASSERT((boost::is_same<VertexBuffer, float*>));
    //    data = &vb;
    //}


    template<class T>
    struct device_
    {
        
        device_()
        {
        }
        template<class T>
        void set(const T&);


        template< >
        void set<>(const Viewport& viewport)
        {
            glViewport(0,0,500,500);
        };


//        void set(const Perspective& per)
//        {
//            glMatrixMode(GL_PROJECTION); 
//            glLoadIdentity();    
//            gluOrtho2D(-1.f,1.f,-1.f,1.f);
//        }
//
//        void set(const GPUProgram& gpu)
//        {
//            GLuint hVS=glCreateShader(GL_VERTEX_SHADER);
//            GLuint hFS=glCreateShader(GL_FRAGMENT_SHADER);
//            static const char* verScrc="void main(void){ gl_Position = ftransform();}";
//            static const char* fraScrc="void main(void){ gl_FragColor = vec4( 0.4, 0.0, 0.9, 1.0 );}";
//            glShaderSource(hVS,1,&verScrc,0);
//            glShaderSource(hFS,1,&fraScrc,0);
//            glCompileShader(hVS);
//            glCompileShader(hFS);
//#ifdef _DEBUG
//            int VertCompiled=0;
//            int FragCompiled=0;
//            glGetShaderiv(hVS,GL_COMPILE_STATUS,&VertCompiled);
//            glGetShaderiv(hFS,GL_COMPILE_STATUS,&FragCompiled);
//            if(!VertCompiled || !FragCompiled) 
//            {
//                std::cout<<"Compiled Fail!!"<<std::endl;
//                assert(0);
//            }
//#endif
//            GLuint Prog=glCreateProgram();
//            glAttachShader(Prog,hVS);
//            glAttachShader(Prog,hFS);
//            glLinkProgram(Prog);
//#ifdef _DEBUG
//            int Linked=0;
//            glGetProgramiv(Prog,GL_LINK_STATUS,&Linked);
//            if(!Linked) {
//                std::cout<<"Link failed!"<<std::endl;
//                assert(0);
//            }
//#endif 
//            glUseProgram(Prog);
//        }
//        void set(const VertexBuffer& vb)
//        {
//
//        }


    };


    typedef device_<OpenGL> Device;


//   typedef boost::mpl::vector<OpenGLDevice> DeviceList;
//    typedef boost::mpl::deref<boost::mpl::find<DeviceList, OpenGLDevice>::type>::type Device;
    


inline void set(Device& device)
{
int major=0;
int minor=0;
const char *version=(const char *)glGetString(GL_VERSION);
if( !version ) 
{
std::cout<<"Can not get the Version!"<<std::endl;
assert(0);
}
sscanf_s(version,"%d.%d",&major,&minor);
if(!(major>=2 && minor>=0)) //OpenGL 2.0 or later
{
std::cout<<"Invalid openGL version!!"<<std::endl;
assert(0);
}
glCreateShader = (PFNGLCREATESHADERPROC) wglGetProcAddress("glCreateShader");
glShaderSource = (PFNGLSHADERSOURCEPROC) wglGetProcAddress("glShaderSource");
glCompileShader = (PFNGLCOMPILESHADERPROC) wglGetProcAddress("glCompileShader");
glGetShaderiv = (PFNGLGETSHADERIVPROC) wglGetProcAddress("glGetShaderiv");
glCreateProgram = (PFNGLCREATEPROGRAMPROC) wglGetProcAddress("glCreateProgram");
glAttachShader = (PFNGLATTACHSHADERPROC) wglGetProcAddress("glAttachShader");
glLinkProgram = (PFNGLLINKPROGRAMPROC) wglGetProcAddress("glLinkProgram");
glGetProgramiv = (PFNGLGETPROGRAMIVPROC) wglGetProcAddress("glGetProgramiv");
glUseProgram = (PFNGLUSEPROGRAMPROC) wglGetProcAddress("glUseProgram");
glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC) wglGetProcAddress("glGetUniformLocation");
glUniform1f = (PFNGLUNIFORM1FPROC) wglGetProcAddress("glUniform1f");
glUniform2f = (PFNGLUNIFORM2FPROC) wglGetProcAddress("glUniform2f");
glUniform3f = (PFNGLUNIFORM3FPROC) wglGetProcAddress("glUniform3f");
glUniform4f = (PFNGLUNIFORM4FPROC) wglGetProcAddress("glUniform4f");
glUniform1i = (PFNGLUNIFORM1IPROC) wglGetProcAddress("glUniform1i");
glDeleteShader = (PFNGLDELETESHADERPROC)wglGetProcAddress("glDeleteShader");
glGenBuffers = (PFNGLGENBUFFERSPROC)wglGetProcAddress("glGenBuffers");
glBindBuffer = (PFNGLBINDBUFFERPROC)wglGetProcAddress("glBindBuffer");
glBufferData = (PFNGLBUFFERDATAARBPROC)wglGetProcAddress("glBufferData");


glShadeModel(GL_SMOOTH);  
glClearColor(0.0f, 0.0f, 1.0f, 0.5f); 
glClearDepth(1.0f);         
glDisable(GL_DEPTH_TEST);   
}
    




///////////////////////////////////////////////////////////////////////////////////    
    template<class E, class T>
    inline void set(const E& element,T& device)
    {
     //   BOOST_MPL_ASSERT(bl::find);
        device.set(element);
    }


 //   template<class T>
 //   inline void set(const T& , Device& device);


//template<>
//inline void set<GPUProgram>(const GPUProgram& gpuprogram, Device& device)
//{
 //       device.set(gpuprogram);
//}
 //   
 //   template<>
 //   inline void set<Viewport>(const Viewport& viewport, Device& device)
 //   {
 //       device.set(viewport);
 //   }


//template<>
//inline void set<Perspective>(const Perspective& perspective, Device& device)
//{
 //       device.set(perspective);
//}
////////////////////////////////////////////////////////////////////////////////////////

    struct Something
    {
        Something()
        {
        }
    };


inline void draw(Something& something)
{   
        glMatrixMode(GL_MODELVIEW);  
        glLoadIdentity();   
        glDisable(GL_DEPTH_TEST);
        glClearColor(0.f, 0.f, 1.f, 1.f);
        glClear(GL_COLOR_BUFFER_BIT);


        float data[] = {1.0, 0.0, 1.0, 0.0, 0.0, -1.0, -1.0, 0.0, 1.0}; 


        GLuint triangleVBO;
        glGenBuffers(1, &triangleVBO);
        glBindBuffer(GL_ARRAY_BUFFER, triangleVBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
        glVertexPointer(3, GL_FLOAT, 0, NULL);  
        glBindBuffer(GL_ARRAY_BUFFER, triangleVBO);
        glEnableClientState(GL_VERTEX_ARRAY);


        glDrawArrays(GL_TRIANGLES, 0, sizeof(data) / sizeof(float) / 3);
        glutSwapBuffers ( );
}  
    
}






#endif

